// #pragma once
// #include <iostream>
// #include <pthread.h>
// #include <stdio.h>
// #include <ctime>
// #include <unistd.h>
// #include <queue>

// //实现一个简单的阻塞队列,只是实现生产者生产数据,消费者消费数据

// namespace qds
// {
//     const int default_capacity = 5;
//     template <class T>
//     class block_queue
//     {
//     public:
//         block_queue(int capacity = default_capacity)
//             : _capacity(capacity)
//         {
//             pthread_mutex_init(&_mutex, nullptr);
//             pthread_cond_init(&_is_full, nullptr);
//             pthread_cond_init(&_is_empty, nullptr);
//         }

//         ~block_queue()
//         {
//             pthread_mutex_destroy(&_mutex);
//             pthread_cond_destroy(&_is_full);
//             pthread_cond_destroy(&_is_empty);
//         }

//         void push(const int &data_in)
//         {
//             lock_queue();
//             //如果队列满了,就不在进行生产了,让消费者来进行消费
//             if (is_full()) //这里使用if可能会有bug
//             {
//                 //这里是线程进行等待的地方,把线程进行了挂起的,但是这个时候线程是持有锁的
//                 //所有让其他的线程使用不到锁,所以pthread_cond_wait会释放锁的
//                 producter_wait();
//             }

//             _bq.push(data_in);


//             if (_bq.size() == default_capacity)
//             {
//                 wakeup_comsumer();
//             }

//             unlock_queue();
//         }

//         void pop(T* data_out)
//         {
//             lock_queue();
//             if (is_empty()) //这里使用if可能会有bug
//             {
//                 comsumer_wait();
//             }

//             sleep(1);
//             *data_out = _bq.front();
//             _bq.pop();

//             if (_bq.size() == 0)
//             {
//                 wakeup_producer();
//             }

//             unlock_queue();
//         }

//     private:
//         void lock_queue()
//         {
//             pthread_mutex_lock(&_mutex);
//         }

//         void unlock_queue()
//         {
//             pthread_mutex_unlock(&_mutex);
//         }

//         bool is_full()
//         {
//             return _bq.size() == _capacity;
//         }

//         bool is_empty()
//         {
//             return _bq.size() == 0;
//         }

//         void producter_wait()
//         {
//             //pthread_cond_wait调用的时候,会首先释放_mutex,然后再挂起自己
//             //pthread_cond_wait返回的时候,会自己竞争_mutex,获取到锁之后,才能返回
//             pthread_cond_wait(&_is_empty, &_mutex);
//         }

//         void comsumer_wait()
//         {
//             pthread_cond_wait(&_is_full, &_mutex);
//         }

//         void wakeup_comsumer()
//         {
//             pthread_cond_signal(&_is_full);
//         }

//         void wakeup_producer()
//         {
//             pthread_cond_signal(&_is_empty);
//         }

//     private:
//         std::queue<T> _bq; //阻塞队列
//         int _capacity; //阻塞队列的容量
//         pthread_mutex_t _mutex; //保护临界资源的锁
//         //1.当生产物品满了的时候,就应该不要生产了(不要竞争锁了),应该让消费者来进行消费
//         //2.当消费者把物品消费空了,就不应该消费了(不要竞争锁了),应该让生产者来进行生产
//         pthread_cond_t _is_full;  //_bq满的时候,消费者应该来消费了,所以消费者应该在该条件变量下进行等待
//         pthread_cond_t _is_empty; //_bq空的时候,生产者应该来生产了,所以生产者应在在该条件变量下进行等待
//     };
// }

//实际我们使用的场景肯定不局限于,一个生产数据,一个获取数据
//生产和获取数据只是第一步,我们怎么生产这些数据,我们获取这些数据过后,怎么处理是关键
//所以这里我们实现一个可以简单生产任务和处理任务的阻塞队列
#pragma once
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <ctime>
#include <unistd.h>
#include <queue>

namespace qds
{
    const int default_capacity = 5;
    template <class T>
    class block_queue
    {
    public:
        block_queue(int capacity = default_capacity)
            : _capacity(capacity)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_is_full, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
        }

        ~block_queue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_is_full);
            pthread_cond_destroy(&_is_empty);
        }

        //const& :输入型参数
        //*: 输出型参数
        //&: 输入输出型参数
        void push(const T &data_in)
        {
            lock_queue();
            //如果队列满了,就不在进行生产了,让消费者来进行消费
            while (is_full()) //使用while就不会有bug
            {
                //这里是线程进行等待的地方,把线程进行了挂起的,但是这个时候线程是持有锁的
                //所有让其他的线程使用不到锁,所以pthread_cond_wait会释放锁的
                producter_wait();
            }

            sleep(1);
            _bq.push(data_in);


            if (is_full())
            {
                wakeup_comsumer();
            }

            unlock_queue();
        }

        void pop(T* data_out)
        {
            lock_queue();
            while (is_empty()) //使用while就不会有bug
            {
                //为什么使用if会有bug的可能?
                //1.wait调用失败了,挂起失败了,这种情况下线程被"唤醒"了一样可以继续向下执行
                //但是由于没有进行二次条件判断，所以在条件不满足的情况下进行生产或消费必然会导致问题！
                //2.伪唤醒,由于系统的原因,线程被误唤醒
                //3.使用pthread_cond_broadcast()唤醒了一批线程
                //但是事实上生产者生产的数据量小于唤醒的线程量，那么就会导致过量消费！
                comsumer_wait();
            }

            sleep(1);
            *data_out = _bq.front();
            _bq.pop();

            if (is_empty())
            {
                wakeup_producer();
            }

            unlock_queue();
        }

    private:
        void lock_queue()
        {
            pthread_mutex_lock(&_mutex);
        }

        void unlock_queue()
        {
            pthread_mutex_unlock(&_mutex);
        }

        bool is_full()
        {
            return _bq.size() == _capacity;
        }

        bool is_empty()
        {
            return _bq.size() == 0;
        }

        void producter_wait()
        {
            //pthread_cond_wait调用的时候,会首先释放_mutex,然后再挂起自己
            //pthread_cond_wait返回的时候,会自己竞争_mutex,获取到锁之后,才能返回
            pthread_cond_wait(&_is_empty, &_mutex);
        }

        void comsumer_wait()
        {
            pthread_cond_wait(&_is_full, &_mutex);
        }

        void wakeup_comsumer()
        {
            pthread_cond_signal(&_is_full);
        }

        void wakeup_producer()
        {
            pthread_cond_signal(&_is_empty);
        }

    private:
        std::queue<T> _bq; //阻塞队列
        int _capacity; //阻塞队列的容量
        pthread_mutex_t _mutex; //保护临界资源的锁
        //1.当生产物品满了的时候,就应该不要生产了(不要竞争锁了),应该让消费者来进行消费
        //2.当消费者把物品消费空了,就不应该消费了(不要竞争锁了),应该让生产者来进行生产
        pthread_cond_t _is_full;  //_bq满的时候,消费者应该来消费了,所以消费者应该在该条件变量下进行等待
        pthread_cond_t _is_empty; //_bq空的时候,生产者应该来生产了,所以生产者应在在该条件变量下进行等待
    };
}
